package vincent.com.layouttest;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

public class BottomSlidingDrawer extends FrameLayout implements AnimatorListener{
	private String TAG = "SlidingDrawer ";
	private LinearLayout mDrawer;
	DrawerState mCurrentState;

	int DRAG_BAR_VALUE;
	boolean isAniating; //是否正在运行动画

	private int mOrgDrawerTop;
	private int mContainerMeasuredH;
	private int mContainerMeasuredW;
	private int mDrawerMeasuredH;
	private int mDrawerMeasuredW;
	private int mOriginalY = 0;
	private boolean mIsDragingDrawer = false;
	private int mOffsetH = 0;
	private DrawerState mInitState = DrawerState.Bottom;
	private OnSlidingListener mSlidingListener;

	public BottomSlidingDrawer(Context context) {
		this(context, null);
	}

	public BottomSlidingDrawer(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public BottomSlidingDrawer(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
//		setBackgroundColor(Color.rgb(82, 173, 230));
		LayoutInflater.from(context).inflate(R.layout.sliding_drawer, this, true);
		DRAG_BAR_VALUE = (int)(context.getResources().getDisplayMetrics().density * 35);
	}

	public OnSlidingListener getSlidingListener() {
		return mSlidingListener;
	}

	public void setSlidingListener(OnSlidingListener mSlidingListener) {
		this.mSlidingListener = mSlidingListener;
	}

	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		Log.i(TAG, "onFinishInflate");
		mDrawer = (LinearLayout)findViewById(R.id.drawer_layout);
		LayoutParams params = (LayoutParams)mDrawer.getLayoutParams();
		params.gravity = Gravity.BOTTOM;
		mDrawer.setLayoutParams(params);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Log.i(TAG, "onTouchEvent");

		if(isAniating)
			return true;

		int y = (int)event.getY();
		switch(event.getAction()){
			case MotionEvent.ACTION_DOWN:
				Log.i(TAG, "onTouchEvent ACTION_DOWN");
				mOriginalY = y;
				if(mContainerMeasuredH - y < DRAG_BAR_VALUE){
					mIsDragingDrawer = true;
					int left = 0;
					int top = mDrawerMeasuredH - DRAG_BAR_VALUE + mOffsetH;
					int right = mDrawer.getWidth();
					int bottom = mDrawerMeasuredH + mOffsetH;
					Log.i(TAG, "onBottomEdge l=" + left + " | t=" + top + " | r=" + right + " | b=" + bottom);
					mDrawer.layout(left, top, right, bottom);
					mDrawer.requestLayout();

					if(mSlidingListener != null){
						mSlidingListener.onSlidingStart(bottom - top, mDrawerMeasuredH);
					}
				}

				break;
			case MotionEvent.ACTION_MOVE:
				Log.i(TAG, "onTouchEvent ACTION_MOVE");
				if(!mIsDragingDrawer){
					break;
				}

				mCurrentState = DrawerState.Moving;
				int left = 0;
				int top = mDrawerMeasuredH - DRAG_BAR_VALUE - (mOriginalY - y) + mOffsetH;
				int right = mDrawer.getWidth();
				int bottom = mDrawerMeasuredH + mOffsetH;

				if(bottom > mContainerMeasuredH){
					bottom = mContainerMeasuredH;
				}

				if(top < mOffsetH){
					top = mOffsetH;
				}

				mDrawer.layout(left, top, right, bottom);
				mDrawer.requestLayout();

				if(mSlidingListener != null)
				{
					mSlidingListener.onSliding(bottom - top, mDrawerMeasuredH);
				}
				break;
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_OUTSIDE:
				Log.i(TAG, "onTouchEvent ACTION_UP|ACTION_CANCEL|ACTION_OUTSIDE");
				if(!mIsDragingDrawer){
					break;
				}
				doAnimateMove();
				mOriginalY = 0;
				mIsDragingDrawer = false;

				if(mSlidingListener != null){
					if(DrawerState.Bottom == mCurrentState){
						mSlidingListener.onSlidingEnd(0, mDrawerMeasuredH);
					}else{
						mSlidingListener.onSlidingEnd(mDrawerMeasuredH, mDrawerMeasuredH);
					}
				}
				break;
		}
		return true;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		Log.i(TAG, "onMeasure");
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		Log.i(TAG, "onDraw");
		super.onDraw(canvas);
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		Log.i(TAG, "onSizeChanged newWidth=" + w + " | newHeight=" + h + " | oldWidth=" + oldw + " | oldh=" + oldh);
		mContainerMeasuredH = h;
		mContainerMeasuredW = w;
		LayoutTouchListener touchListener = new LayoutTouchListener();
		mDrawer.setOnTouchListener(touchListener);
		mDrawerMeasuredH = mDrawer.getMeasuredHeight();
		mDrawerMeasuredW = mDrawer.getMeasuredWidth();
		mOffsetH = mContainerMeasuredH - mDrawerMeasuredH;
		Log.i(TAG, "mDrawerMeasuredH=" + mDrawerMeasuredH + " | mDrawerMeasuredW=" + mDrawerMeasuredW + " | mOffsetH=" + mOffsetH);

		if(DrawerState.Bottom == mInitState){
			mDrawer.layout(0, mOffsetH + mDrawerMeasuredH, mContainerMeasuredW, mContainerMeasuredH);
			mCurrentState = DrawerState.Bottom;
		}else if(DrawerState.Top == mInitState){
			mDrawer.layout(0, mOffsetH, mContainerMeasuredW, mContainerMeasuredH);
			mCurrentState = DrawerState.Top;
		}

	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
							int bottom) {
		Log.i(TAG, "onLayout");
		int l = mDrawer.getLeft();
		int t = mDrawer.getTop();
		int r = mDrawer.getRight();
		int b = mDrawer.getBottom();

		super.onLayout(changed, left, top, right, bottom);

		if(!(l == 0 && t == 0 && r == 0 && b == 0) && t <= b){
			mDrawer.layout(l, t, r, b);
		}
	}

	class LayoutTouchListener implements OnTouchListener/*, AnimatorListener*/ {

		int lastY;
		int orgY;

		boolean isPressing;
		long pressTimeMillis;

		public LayoutTouchListener() {
		}

		public boolean onTouch(View v, MotionEvent event) {

			if(isAniating)
				return true;

			int left, top, right, bottom;
			int ea = event.getAction();
			int currentY = (int)event.getRawY();
			Log.i(TAG, "onTouch currentY=" + currentY);
			switch (ea) {

				case MotionEvent.ACTION_DOWN: // 按下
					Log.i(TAG, " ACTION_DOWN");

					orgY = lastY = currentY;
					isPressing = true;

					mOrgDrawerTop = mDrawer.getTop();
					pressTimeMillis = System.currentTimeMillis();

					left = 0;
					top = mDrawer.getTop();
					right = mDrawer.getWidth();
					bottom = mDrawer.getTop() + mDrawerMeasuredH;

					mDrawer.layout(left, top, right, bottom);
					mDrawer.requestLayout();

					if(mSlidingListener != null){
						mSlidingListener.onSlidingStart(bottom - top, mDrawerMeasuredH);
					}
					return true;

				case MotionEvent.ACTION_MOVE: // 移动
					Log.i(TAG, " ACTION_MOVE");
					if (!isPressing) {
						return false;
					}

					mCurrentState = DrawerState.Moving;

					left = 0;
					top = mOrgDrawerTop + (currentY - orgY);
					right = mDrawer.getWidth();
					bottom = mDrawerMeasuredH + top;

					if(bottom > mContainerMeasuredH){
						bottom = mContainerMeasuredH;
					}

					if (top < mOffsetH) {
						top = mOffsetH;
					}

					if(bottom < mDrawerMeasuredH + mOffsetH){
						bottom = mDrawerMeasuredH + mOffsetH;
					}

					mDrawer.layout(left, top, right, bottom);
					mDrawer.requestLayout();

					lastY = currentY;

					if(mSlidingListener != null){
						mSlidingListener.onSliding(bottom - top, mDrawerMeasuredH);
					}
					return true;

				case MotionEvent.ACTION_UP:
				case MotionEvent.ACTION_CANCEL:
				case MotionEvent.ACTION_OUTSIDE:
					Log.i(TAG, " ACTION_UP | ACTION_CANCEL | ACTION_OUTSIDE");

					doAnimateMove();
					isPressing = false;

					if(mSlidingListener != null){
						if(DrawerState.Bottom == mCurrentState){
							mSlidingListener.onSlidingEnd(0, mDrawerMeasuredH);
						}else{
							mSlidingListener.onSlidingEnd(mDrawerMeasuredH, mDrawerMeasuredH);
						}
					}
					return true;

			}
			return true;
		}
	}

	/**
	 * do the animation by the down and up Y position
	 */
	void doAnimateMove(){
		int currentTop = mDrawer.getTop();
		int standard = mContainerMeasuredH - (mDrawerMeasuredH >> 1);

		if(currentTop >= standard && (DrawerState.Top == mCurrentState || DrawerState.Moving == mCurrentState)){//bottom
			move2Bottom(300);
		}else if(currentTop < standard && (DrawerState.Bottom == mCurrentState || DrawerState.Moving == mCurrentState)){
			move2Top(300);
		}
	}

	void move2Bottom(int duration){
		Log.i(TAG, "move2Bottom");
		mCurrentState = DrawerState.Bottom;
		ObjectAnimator ani;

		int offTemp = mDrawerMeasuredH - mDrawer.getTop();
		ani = ObjectAnimator.ofFloat(mDrawer, "translationY", 0, mDrawerMeasuredH).setDuration(duration);
		ani.setInterpolator(new AccelerateDecelerateInterpolator());
		ani.addListener(this);
		ani.start();
	}

	void move2Top(int duration){
		int releaseTop = mDrawer.getTop();

		Log.i(TAG, "move2Top");

		int left = 0;
		int top = releaseTop;
		int right = mDrawer.getWidth();
		int bottom = releaseTop + mDrawerMeasuredH;
		mDrawer.layout(left, top, right, bottom);
		mDrawer.requestLayout();

		mCurrentState = DrawerState.Top;
		ObjectAnimator ani;

		int offTemp = Math.abs(releaseTop - mOffsetH);
		ani = ObjectAnimator.ofFloat(mDrawer, "translationY", 0, -offTemp).setDuration(duration);
		ani.setInterpolator(new AccelerateDecelerateInterpolator());
		ani.addListener(this);
		ani.start();
	}

	@Override
	public void onAnimationCancel(Animator arg0) {
		isAniating = false;
	}

	@Override
	public void onAnimationEnd(Animator arg0) {

		isAniating = false;
		switch(mCurrentState){
			case Top:
				int left = 0;
				int top = mOffsetH;
				int right = mDrawer.getWidth();
				int bottom = mDrawerMeasuredH + mOffsetH;
				mDrawer.layout(left, top, right, bottom);

				mDrawer.setTranslationY(0);
				break;
			case Bottom:
				mDrawer.layout(0, mDrawerMeasuredH, mDrawer.getWidth(), mDrawerMeasuredH);
				mDrawer.setTranslationY(0);
				break;
		}

	}

	@Override
	public void onAnimationRepeat(Animator arg0) {

	}

	@Override
	public void onAnimationStart(Animator arg0) {
		isAniating = true;
	}

	public interface OnSlidingListener{
		public void onSlidingStart(int slidingHeight, int totalHeight);
		public void onSliding(int slidingHeight, int totalHeight);
		public void onSlidingEnd(int slidingHeight, int totalHeight);
	}

	enum DrawerState{
		Top,
		Bottom,
		DrawerState, Moving
	}
}
